MongoDB module (mongo)
Start here
bring mongo connects Flow-Wing programs to a MongoDB server via the official libmongoc C driver. The module exposes three classes — mongo::Client, mongo::Collection, and mongo::Cursor — plus a free mongo::lastError() for diagnostics. Documents go in and out as JSON strings, so it pairs naturally with the json module.
Prerequisites: a reachable
mongod(defaultmongodb://127.0.0.1:27017) and a Flow-Wing build with the bundledlibmongocdependency.
A tiny program
bring mongo
var uri: str = "mongodb://127.0.0.1:27017"
var mc: mongo::Client = new mongo::Client(uri)
if !mc.isOk() {
println("connect failed: ", mongo::lastError())
return :
}
var events: mongo::Collection = mc.getCollection("app", "events")
var doc: str = "{\"k\":\"hi\",\"n\":1}"
var ok: bool = events.insertOne(doc)
println("inserted: ", ok)
mc.close()
Note on string literals: Flow-Wing uses double quotes for strings. Escape inner double quotes with backslash when embedding JSON:
"{\"key\":\"val\"}".
Connecting
bring mongo
var mc: mongo::Client = new mongo::Client("mongodb://127.0.0.1:27017")
if !mc.isOk() {
println("connect failed: ", mongo::lastError())
return :
}
/; ... use mc ...
mc.close()
new mongo::Client(uri) parses the URI eagerly. isOk() returns false when the URI is invalid or the handle table is full — read mongo::lastError() for the message. Always call close() when done; it destroys the client and every collection/cursor handle the program has opened (the v1 driver uses a single shared handle table).
Getting a collection
bring mongo
var mc: mongo::Client = new mongo::Client("mongodb://127.0.0.1:27017")
var users: mongo::Collection = mc.getCollection("app", "users")
var events: mongo::Collection = mc.getCollection("app", "events")
getCollection(db, coll) is cheap and does not round-trip to the server — collections are created lazily on first write.
Inserting
One document
bring mongo
var coll: mongo::Collection = mc.getCollection("app", "events")
var doc: str = "{\"k\":\"signup\",\"n\":1}"
var ok: bool = coll.insertOne(doc)
if !ok {
println("insertOne failed: ", mongo::lastError())
}
Many documents
Pass a JSON array of documents — each element must be an object.
bring mongo
var batch: str = "[{\"k\":\"a\",\"n\":1},{\"k\":\"b\",\"n\":2}]"
var ok: bool = coll.insertMany(batch)
if !ok {
println("insertMany failed: ", mongo::lastError())
}
Counting
bring mongo
var n: int64 = coll.count("{\"k\":\"signup\"}")
println("matched: ", n)
/; count all documents — empty filter "" is treated as {}
var total: int64 = coll.count("")
Returns -1 on error; check mongo::lastError().
Reading
Single document
bring mongo
var hit: str = coll.findOne("{\"k\":\"signup\"}")
if hit == "" {
var err: str = mongo::lastError()
if err != "" {
println("findOne error: ", err)
} else {
println("no match")
}
} else {
println("found: ", hit)
}
findOne returns the document as a JSON string in relaxed extended JSON form, or "" for either no match or error — distinguish the two by checking mongo::lastError().
Cursor
For multiple results, open a cursor with find(filterJson, limit) (pass limit = 0 for no limit). Read with next() until it returns "", then close().
find has no order — pair every limit with findSortedfind returns documents in natural order, which is roughly insertion order:
oldest first. Combined with a limit that means you get the oldest n
documents, not the newest.
This stays invisible while a collection holds fewer than limit documents,
because "the oldest n" and "all of them" are the same set. The moment it grows
past limit, the same query silently starts returning stale data — and keeps
returning it as new documents pile up behind the cap.
Use findSorted whenever you pass a limit.
bring mongo
var cur: mongo::Cursor = coll.find("{\"k\":\"signup\"}", 100)
var s: str = cur.next()
while s != "" {
println(s)
s = cur.next()
}
cur.close()
Always
close()cursors to release the server-side cursor and the local handle slot.mc.close()also clears every open cursor as a safety net.
Sorted cursor
findSorted(filterJson, limit, sortJson) is find with an explicit order.
sortJson is a Mongo sort document: -1 is descending, 1 ascending.
bring mongo
/; the 100 MOST RECENT signups, not the 100 oldest
var cur: mongo::Cursor = coll.findSorted("{\"k\":\"signup\"}", 100, "{\"ts\": -1}")
var s: str = cur.next()
while s != "" {
println(s)
s = cur.next()
}
cur.close()
With a sort in place, hitting the limit drops the oldest matches — the only
end that is safe to lose.
A malformed sortJson is reported through mongo::lastError() and returns an
empty cursor. It is never ignored, because silently dropping the sort would put
the oldest-first behaviour back with nothing to reveal it.
Error handling
There are no exceptions. Every operation reports failure through its return value (false, 0, -1, or "") and writes a thread-local string readable via mongo::lastError(). The last-error slot is cleared at the start of every call, so read it immediately after a failing call.
bring mongo
var ok: bool = coll.insertOne("not json")
if !ok {
println("insert failed: ", mongo::lastError())
}
mongo::Client
| Method | Returns | Description |
|---|---|---|
new mongo::Client(uri) | mongo::Client | Opens a client from a MongoDB connection URI |
.isOk() | bool | true when the handle is valid |
.getCollection(db, coll) | mongo::Collection | Borrows a collection handle from this client |
.close() | — | Destroys this client and every open collection/cursor |
mongo::Collection
| Method | Returns | Description |
|---|---|---|
.insertOne(json) | bool | Inserts one JSON document |
.insertMany(jsonArr) | bool | Inserts each element of a JSON array of documents |
.findOne(filterJson) | str | First match as JSON, or "" for none/error |
.find(filterJson, limit) | mongo::Cursor | Opens a cursor in natural order (oldest first); limit = 0 means unlimited |
.findSorted(filterJson, limit, sortJson) | mongo::Cursor | Same, with an explicit sort — e.g. "{\\"ts\\": -1}" for newest first |
.count(filterJson) | int64 | Document count; -1 on error; "" filter = {} |
mongo::Cursor
| Method | Returns | Description |
|---|---|---|
.next() | str | Next document as JSON; "" at end-of-stream or error |
.close() | — | Releases the cursor handle |
Module function
| Function | Returns | Description |
|---|---|---|
mongo::lastError() | str | Most recent error message (thread-local); "" when none |
Limits & gotchas
- Single-threaded model — handle tables are global; safe for Flow-Wing's single-thread runtime, not for sharing handles across threads.
- Handle caps — up to 64 clients, 256 collections, 256 cursors per process.
mc.close()is "close-all" — it destroys every open collection and cursor along with the client.- JSON in, JSON out — documents cross the FFI as relaxed extended JSON strings; use the
jsonmodule to build or parse them. limit = 0onfindreturns the full result set; oncountthe filter""is treated as{}.
Source & tests (if you have the repository)
| What | Where |
|---|---|
| Module source | fw-modules/mongo_module/mongo-module.fg |
| Native bridge | fw-modules/mongo_module/libflowwing_mongo.cpp |
| Local smoke test | tests/local/mongo_test.fg |